﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ElevatorDispatching
{
    /// <summary>
    /// 电梯群组信号处理器的默认实现。
    /// 该处理器将待处理的呼层信号优先发送给距离最近的空闲轿厢或可执行顺向截梯的轿厢，
    /// 如无符合条件的轿厢，则将信号加入等待队列，并在有轿厢转入空闲状态或翻转运行方向时，
    /// 指示该轿厢处理等待队列中等待时间最久的可响应呼梯信号及其顺向信号。
    /// </summary>
    public class DefaultMultiCageSignalHandler : SignalHandlerBase
    {
        /// <summary>
        /// 记录每个轿厢当前运行过程的目标楼层，以及到达目标楼层后响应呼梯信号的方向，
        /// 用以判断其他楼层发出的呼梯信号是否满足顺向截梯的条件。
        /// </summary>
        private IDictionary<Elevator, (Direction Direction, int FloorId)> _responded;

        /// <summary>
        /// 呼梯信号的等待队列，用以记录尚未分配轿厢的呼梯信号，并在有轿厢转入空闲状态或翻转运行方向时处理。
        /// </summary>
        private IDictionary<(Direction Direction, int FloorId), CallCageSignal> _notResponded;

        public DefaultMultiCageSignalHandler(IFloorGroup floors, IElevatorGroup elevators)
            : base(floors, elevators)
        {
            _responded = new Dictionary<Elevator, (Direction, int)>();
            _notResponded = new Dictionary<(Direction, int), CallCageSignal>();

            foreach (var elevator in elevators)
            {
                elevator.PropertyChanged += RunningDirectionChanged;
            }
        }

        private void RunningDirectionChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!(sender is Elevator elevator)) return;
            if (e.PropertyName != nameof(Elevator.RunningDirection)) return;

            // 运行方向发生变化包含了轿厢转入空闲状态和翻转运行方向两种情况。
            var signals = _notResponded.Values.OrderBy(x => x.Time).ToList();
            foreach (var signal in signals)
            {
                if (HandleCallCageSignal(signal)) _notResponded.Remove((signal.Direction, signal.FloorId));
            }
        }

        /// <summary>
        /// 处理呼层呼梯信号。由于未处理信号会被移入内部的等待队列稍后处理，因此总是返回 true。
        /// </summary>
        /// <param name="signal">要处理的呼层呼梯信号</param>
        /// <returns>返回值指示该信号是否被处理</returns>
        public override bool HandleCallSignal(ICallSignal signal)
        {
            base.HandleCallSignal(signal);
            return true;
        }

        /// <summary>
        /// 处理呼梯信号，返回值指示该信号是否被成功处理。
        /// </summary>
        /// <param name="signal">呼梯信号</param>
        /// <returns>指示该信号是否被成功处理</returns>
        protected override bool HandleCallCageSignal(CallCageSignal signal)
        {
            var floor = Floors[signal.FloorId];
            var direction = signal.Direction;
            {
                // 如有同层停靠中的顺向轿厢，则由该轿厢处理该信号
                var cageStoppingAtTheFloor = (from x in Elevators
                                              where x.RunningStatus == RunningStatus.Stopped
                                              where x.CurrentFloor.Id == floor.Id
                                              where x.RunningDirection != direction.Reverse()
                                              orderby x.RunningStatusChangedTime
                                              select x).FirstOrDefault();
                if (cageStoppingAtTheFloor != null)
                {
                    _responded[cageStoppingAtTheFloor] = (direction, floor.Id);
                    cageStoppingAtTheFloor.CallCage(signal);
                    return true;
                }
            }
            {
                // 找出所有空闲轿厢以及运行方向后方与呼梯信号方向相同且正在响应运行方向呼梯请求的轿厢，
                // 由其中距离呼梯信号所在楼层距离最近的轿厢响应该呼梯信号
                var cageAvailable = (from x in Elevators
                                     where x.RunningStatus == RunningStatus.Stopped && x.RunningDirection == Direction.Unspecified
                                        || floor.RelationWith(x.CurrentFloor) == x.RunningDirection
                                        && (!_responded.ContainsKey(x) || _responded[x].Direction == direction)
                                     orderby floor.DistanceTo(x.CurrentFloor)
                                     select x).FirstOrDefault();
                if (cageAvailable != null)
                {
                    _responded[cageAvailable] = (direction, floor.Id);
                    cageAvailable.CallCage(signal);
                    return true;
                }
            }
            {
                // 此刻若无合适的轿厢可以处理该信号，则将该信号移入等待队列
                if (!_notResponded.ContainsKey((direction, floor.Id)))
                {
                    _notResponded[(direction, floor.Id)] = signal;
                }
                return false;
            }
        }

        /// <summary>
        /// 清除特定楼层特定方向上的呼梯信号
        /// </summary>
        /// <param name="floorId">楼层 Id</param>
        /// <param name="direction">呼梯信号的方向</param>
        public override void EraseCallCageSignals(int floorId, Direction direction)
        {
            base.EraseCallCageSignals(floorId, direction);

            var floor = Floors[floorId];

            var handled = _responded.Where(x => x.Value == (direction, floor.Id)).ToList();

            foreach (var item in handled)
            {
                _responded.Remove(item.Key);
            }

            _notResponded.Remove(((direction, floorId)));
        }
    }
}
